package chapter4;

import lombok.extern.slf4j.Slf4j;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

/**
 * @author caiya
 * @date 2020/3/25 20:52
 */
@Slf4j
public class LockSupportTest {
    private static volatile boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        /**
         * LockSupport 是一个工具类，会为每个使用 LockSupport 的线程关联一个许可证
         * LockSupport.park：判断有无许可证，没有的话阻塞挂起，直到获取到了许可证原地返回继续执行
         * LockSupport.unpark(thread)：给thread颁发许可证，如果thread之前因调用park而被挂起的话，那么恢复执行
         *
         * 注意：解除park导致的挂起不仅可以使用unpark，也可以使用interrupt()
         */

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                log.debug(Thread.currentThread().getName() + ": start doing...");
                LockSupport.park();
                log.debug(Thread.currentThread().getName() + ": end doing...");
            }
        }, "线程1");
        thread.start();

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                log.debug(Thread.currentThread().getName() + ": start doing...");
                while (flag) {
                    LockSupport.park();
                }
                log.debug(Thread.currentThread().getName() + ": end doing...");
            }
        }, "线程2");
        thread2.start();

        Thread.sleep(1000);

        log.debug(Thread.currentThread().getName() + ": start doing...");

        // 使得thread线程获取到许可证，那么park方法就会拜托阻塞返回继续执行
        LockSupport.unpark(thread);

        // 解除park的阻塞也可以使用interrupt
         thread.interrupt();

        // flag 为true，线程2还处于挂起状态，unpark没起作用相当于
        LockSupport.unpark(thread2);

        // 修改flag，解除thread2的挂起
        Thread.sleep(1000);

        // 必须设置false，否则即使unpark，thread2也不会返回
        flag = false;
        LockSupport.unpark(thread2);
    }

    /**
     * 实现一个先进先出的锁，只有队首的元素可以获取锁
     */
    static class FIFOMutex {
        private final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
        private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();

        // 上锁
        public void lock() {
            boolean wasInterrupted = false;
            Thread thread = Thread.currentThread();

            waiters.add(thread);
            while (waiters.peek() != thread || !atomicBoolean.compareAndSet(false, true)) {
                LockSupport.park(this);
                if (thread.isInterrupted()) {
                    wasInterrupted = true;
                }
            }

            waiters.remove();
            // 还原中断现场
            if (wasInterrupted) {
                thread.interrupt();
            }
        }

        // 解锁
        public void unlock() {
            atomicBoolean.set(false);
            LockSupport.unpark(waiters.peek());
        }
    }
}

